/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* vim:set et sts=4: */
/* IBust - IBus with Traditional interface
 * Copyright(c) 2013 Daniel Sun
 * Copyright (c) 2010, Google Inc. All rights reserved.
 * Copyright (C) 2010 Peng Huang <shawn.p.huang@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <string.h>
#include <ibust.h>
#include "config.h"


typedef struct _IBustConfigMemconfClass IBustConfigMemconfClass;

struct _IBustConfigMemconf {
    IBustConfigService parent;
    GHashTable *values;
};

struct _IBustConfigMemconfClass {
    IBustConfigServiceClass parent;
};

/* functions prototype */
static void         ibust_config_memconf_class_init  (IBustConfigMemconfClass *class);
static void         ibust_config_memconf_init        (IBustConfigMemconf      *config);
static void         ibust_config_memconf_destroy     (IBustConfigMemconf      *config);
static gboolean     ibust_config_memconf_set_value   (IBustConfigService      *config,
                                                     const gchar            *section,
                                                     const gchar            *name,
                                                     GVariant               *value,
                                                     GError                **error);
static GVariant    *ibust_config_memconf_get_value   (IBustConfigService      *config,
                                                     const gchar            *section,
                                                     const gchar            *name,
                                                     GError                **error);
static GVariant    *ibust_config_memconf_get_values  (IBustConfigService      *config,
                                                     const gchar            *section,
                                                     GError                **error);
static gboolean     ibust_config_memconf_unset_value (IBustConfigService      *config,
                                                     const gchar            *section,
                                                     const gchar            *name,
                                                     GError                **error);

G_DEFINE_TYPE (IBustConfigMemconf, ibust_config_memconf, IBUST_TYPE_CONFIG_SERVICE)

static void
ibust_config_memconf_class_init (IBustConfigMemconfClass *class)
{
    GObjectClass *object_class = G_OBJECT_CLASS (class);

    IBUST_OBJECT_CLASS (object_class)->destroy = (IBustObjectDestroyFunc) ibust_config_memconf_destroy;
    IBUST_CONFIG_SERVICE_CLASS (object_class)->set_value   = ibust_config_memconf_set_value;
    IBUST_CONFIG_SERVICE_CLASS (object_class)->get_value   = ibust_config_memconf_get_value;
    IBUST_CONFIG_SERVICE_CLASS (object_class)->get_values  = ibust_config_memconf_get_values;
    IBUST_CONFIG_SERVICE_CLASS (object_class)->unset_value = ibust_config_memconf_unset_value;
}

static void
ibust_config_memconf_init (IBustConfigMemconf *config)
{
    config->values = g_hash_table_new_full (g_str_hash,
                                            g_str_equal,
                                            (GDestroyNotify)g_free,
                                            (GDestroyNotify)g_variant_unref);
}

static void
ibust_config_memconf_destroy (IBustConfigMemconf *config)
{
    g_hash_table_destroy (config->values);
    IBUST_OBJECT_CLASS (ibust_config_memconf_parent_class)->destroy ((IBustObject *)config);
}

static gboolean
ibust_config_memconf_set_value (IBustConfigService *config,
                               const gchar       *section,
                               const gchar       *name,
                               GVariant          *value,
                               GError           **error)
{
    g_assert (IBUST_IS_CONFIG_MEMCONF (config));
    g_assert (section);
    g_assert (name);
    g_assert (value);
    g_assert (error == NULL || *error == NULL);

    gchar *key = g_strdup_printf ("%s:%s", section, name);

    g_hash_table_insert (IBUST_CONFIG_MEMCONF (config)->values,
                         key, g_variant_ref_sink (value));

    ibust_config_service_value_changed (config, section, name, value);

    return TRUE;
}

static GVariant *
ibust_config_memconf_get_value (IBustConfigService *config,
                               const gchar       *section,
                               const gchar       *name,
                               GError           **error)
{
    g_assert (IBUST_IS_CONFIG_MEMCONF (config));
    g_assert (section);
    g_assert (name);
    g_assert (error == NULL || *error == NULL);

    gchar *key = g_strdup_printf ("%s:%s", section, name);
    GVariant *value = (GVariant *)g_hash_table_lookup (IBUST_CONFIG_MEMCONF (config)->values, key);
    g_free (key);

    if (value != NULL) {
        g_variant_ref (value);
    }
    else if (error != NULL) {
        *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
                              "Config value [%s:%s] does not exist.", section, name);
    }
    return value;
}

static GVariant *
ibust_config_memconf_get_values (IBustConfigService *config,
                                const gchar       *section,
                                GError           **error)
{
    g_assert (IBUST_IS_CONFIG_MEMCONF (config));
    g_assert (section);
    g_assert (error == NULL || *error == NULL);

    GHashTableIter iter;
    const gchar *key;
    GVariant *value;
    
    GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
    g_hash_table_iter_init (&iter, IBUST_CONFIG_MEMCONF (config)->values);
    while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value)) {
        gchar **v = g_strsplit (key, ":", 2);
        if (g_strcmp0 (v[0], section) == 0) {
            g_variant_builder_add (builder, "{sv}", v[1], value); 
        }
        g_strfreev(v);
    }

    return g_variant_builder_end (builder);
}

static gboolean
ibust_config_memconf_unset_value (IBustConfigService *config,
                                 const gchar       *section,
                                 const gchar       *name,
                                 GError            **error)
{
    g_assert (IBUST_IS_CONFIG_MEMCONF (config));
    g_assert (section);
    g_assert (name);
    g_assert (error == NULL || *error == NULL);

    gchar *key = g_strdup_printf ("%s:%s", section, name);
    gboolean retval = g_hash_table_remove (IBUST_CONFIG_MEMCONF (config)->values, key);
    g_free (key);

    if (retval) {
        ibust_config_service_value_changed (config,
                                           section,
                                           name,
                                           g_variant_new_tuple (NULL, 0));
    }
    else {
        g_set_error (error,
                     G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
                     "Config value [%s:%s] does not exist.",
                     section, name);
    }
    return retval;
}

IBustConfigMemconf *
ibust_config_memconf_new (GDBusConnection *connection)
{
    IBustConfigMemconf *config;
    config = (IBustConfigMemconf *) g_object_new (IBUST_TYPE_CONFIG_MEMCONF,
                                                 "object-path", IBUST_PATH_CONFIG,
                                                 "connection", connection,
                                                 NULL);
    return config;
}
